package com.xcb.spring.context;

import com.xcb.spring.aop.AspectJAwareAdvisorAutoProxyCreator;
import com.xcb.spring.aop.BeanFactoryAware;
import com.xcb.spring.beans.*;
import com.xcb.spring.beans.aware.BeanNameAware;
import com.xcb.spring.beans.factory.AbstractBeanFactory;
import com.xcb.spring.beans.factory.AutowireCapableBeanFactory;
import com.xcb.spring.util.BeanUtil;
import com.xcb.spring.util.FileUtil;
import com.xcb.spring.util.StrUtil;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

public class AnnotationConfigApplicationContext extends AbstractApplicationContext{

    private Class<?> configClass;

    public AnnotationConfigApplicationContext(Class configClass,BeanPostProcessor beanPostProcessor) throws Exception {
        super(new AutowireCapableBeanFactory());
        this.configClass = configClass;
        beanPostProcessorInit(beanPostProcessor);
        refresh();
    }

    private void beanPostProcessorInit(BeanPostProcessor beanPostProcessor) throws Exception {
        beanFactory.addBeanPostProcessor(beanPostProcessor);
        if(beanPostProcessor instanceof BeanFactoryAware){
            BeanFactoryAware beanFactoryAware =(BeanFactoryAware)beanPostProcessor;
            beanFactoryAware.setBeanFactory(beanFactory);
        }
    }

    public AnnotationConfigApplicationContext(Class configClass) throws Exception {
        super(new AutowireCapableBeanFactory());
        this.configClass = configClass;
        refresh();
    }


    @Override
    protected void loadBeanDefinitions(AbstractBeanFactory beanFactory) throws Exception {
        this.scan(configClass);
    }

    @Override
    public void addPostProcess(BeanPostProcessor beanPostProcessor) throws Exception {
        beanFactory.addBeanPostProcessor(beanPostProcessor);
    }


    private void scan(Class<?> configClass) throws Exception {
        // 扫描class，转化为BeanDefinition对象，最后添加到beanDefinitionMap中

        // 先得到包路径
        ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
        String packagePath = componentScanAnnotation.value();


        // 扫描包路径得到classList
        List<Class<?>> classList = genBeanClasses(packagePath);
        // 遍历class得到BeanDefinition
        for (Class clazz : classList) {
            if (clazz.isAnnotationPresent(Component.class)) {
                BeanDefinition beanDefinition = new BeanDefinition();
                beanDefinition.setBeanClass(clazz);

                // 要么Spring自动生成，要么从Component注解上获取
                Component component = (Component) clazz.getAnnotation(Component.class);
                String beanName = component.value();
                if(StrUtil.isBlank(beanName)){
                    beanName = BeanUtil.parseBeanName(clazz);
                }
                // 解析scope
                if (clazz.isAnnotationPresent(Scope.class)) {
                    Scope scope = (Scope) clazz.getAnnotation(Scope.class);
                    String scopeValue = scope.value();
                    if (ScopeEnum.singleton.name().equals(scopeValue)) {
                        beanDefinition.setScope(ScopeEnum.singleton);
                    } else {
                        beanDefinition.setScope(ScopeEnum.prototype);
                    }
                } else {
                    beanDefinition.setScope(ScopeEnum.singleton);
                }
                beanFactory.registerBeanDefinition(beanName,beanDefinition);
            }
        }
    }

    private List<Class<?>> genBeanClasses(String packagePath) {
        List<Class<?>> beanClasses = new ArrayList<Class<?>>();

        ClassLoader classLoader = AnnotationConfigApplicationContext.class.getClassLoader();
        packagePath = packagePath.replace(".", "/");
        URL resource = classLoader.getResource(packagePath);
        File file = new File(resource.getFile());

        beanClasses = FileUtil.findClassToDirectory(file,classLoader);

        return beanClasses;
    }



}
